package IC;
import IC.LIR.LIRTranslator;

import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import IC.AST.ICClass;
import IC.AST.PrettyPrinter;
import IC.AST.Program;
import IC.Parser.*;
import IC.SemanticCheckes.SemanticChecker;
import IC.SemanticCheckes.SemanticError;
import IC.SemanticCheckes.SymbolTableBuilder;
import IC.SymbolTable.GlobalSymbolTable;
import IC.Types.TypeTable;
public class Compiler
{
    public static void main(String[] args)
    {
    	/* check parameters */
    	ParamObject param = null;
    	try {
			param = new ParamObject(args);
		} catch (Exception e1) {
			
			System.out.println(e1.getMessage());
			System.exit(-1);
		}
    	
    	java_cup.runtime.Symbol parseMainFile = new java_cup.runtime.Symbol(0);
    	java_cup.runtime.Symbol parseLibrary = new java_cup.runtime.Symbol(1);
    	
    	try {
    		 /* parse library */
    		if (param.getLibraryToLink() != null) {
	    		 FileReader libFile = new FileReader(param.getLibraryToLink());
	    		 Lexer liblex = new Lexer(libFile);
	    		 if (param.shouldPrintTokens()) {
	    		   System.out.println("Lib Tokens:");
	  	           for (String token: parseTokens(param.getLibraryToLink())) {
	  	        	   System.out.println("\t"+token);
	  	           }
	    		 }
	    		 LibraryParser lib_parser = new LibraryParser(liblex);
	    		 parseLibrary = lib_parser.parse();
    		}
    		
    		 /* parse main file */
             FileReader txtFile = new FileReader(param.getFilenameToCompile());
             Lexer mainFileLexer = new Lexer(txtFile);
             if (param.shouldPrintTokens()) {
            	 
	    		 System.out.println("Main Tokens:");
	  	         for (String token: parseTokens(param.getFilenameToCompile())) {
	  	        	 System.out.println("\t"+token);
	  	         }
	    	 }
             Parser mainFile_parser = new Parser(mainFileLexer);
             parseMainFile = mainFile_parser.parse();
             if (param.getLibraryToLink() != null) {
            	 Program mainRoot = (Program)parseMainFile.value;
            	 mainRoot.addLibraryClass((ICClass) parseLibrary.value);
             }
             
             
         } catch (LexicalError q) {
        	 System.out.println(q);
        	 System.exit(-1);
         }
           catch (SyntaxError q) {
        	 System.out.println(q);
        	 System.exit(-1);
         }
      
           catch (Exception e) {
        	 System.out.println(e.getMessage());
             System.exit(-1);
         }
        
        
           /* print the tree using PrettyPrinter */
           if (param.shouldPrintAST()) {
	           PrettyPrinter printer = new PrettyPrinter(param.getFilenameToCompile());
	           System.out.println( ((Program) parseMainFile.value).accept(printer));
           }
           
           /* now build the symbol table */
           SymbolTableBuilder STBuilder = new SymbolTableBuilder(param.getFilenameToCompile());
           GlobalSymbolTable gst = null;
           try {
        	   gst =(GlobalSymbolTable) STBuilder.visit((Program) parseMainFile.value);
           } 
           catch (SemanticError se) {
//        	   System.err.println("Symbol Table Build Phase Failed");
        	   System.err.println(se);
        	   System.exit(-1);
           }
         
           
           /* Implement checking */
           SemanticChecker SMChecker = 
        	   new SemanticChecker(gst);
           try {
        	   SMChecker.visit((Program) parseMainFile.value);
           }
           catch (SemanticError se) {
//        	   System.err.println("Semantic Checker Phase Failed");
        	   System.err.println(se);
        	   System.exit(-1);
        	   
           }
           
           /* print the symbol table using PrettyPrinter */
           if (param.shouldPrintSymTable())
           {
				System.out.println("\n"+gst);
				System.out.println(TypeTable.PrintSymTable());       	   
           }
           /* LIR part - translate the code to LIR */
           if (param.shouldPrintLIR()) {
				LIRTranslator translator = 
					new LIRTranslator(gst);
				String tr = ((Program) parseMainFile.value)
							.accept(translator, 0).getLIRCode();
				System.out.println(tr);
				BufferedWriter writer;
				try {
					writer = new BufferedWriter(
							new FileWriter(param.getLIRname()));
					writer.write(tr);
					writer.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
           }
			
           
    }
    /**
     * simple function that dumps the tokens to file
     * used for testing purposes
     * @param filename - valid path to output file
     */
    public static void toFile(String filename) {
    	try {
    		
			PrintWriter output = new PrintWriter(filename.replace(".ic", ".exp"));
			List<String> tokens = parseTokens(filename);
			for (String token : tokens) {
				output.println(token);
			}
			output.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
    	
    }
    /**
     * 
     * @param filename - valid path to IC file
     * @return list of tokens
     */
    public static List<String> parseTokens(String filename) {
    	Token currToken;
    	List<String> tokens = new ArrayList<String>();
    	
        try {
            FileReader txtFile = new FileReader(filename);
            Lexer scanner = new Lexer(txtFile);
            do {
                currToken = scanner.next_token();
                // do something with currToken
                tokens.add(currToken.toString());
  
               
            } while (currToken.sym != sym.EOF);
        
        } catch (LexicalError q) {
        	tokens.add(q.toString());
        	
        }
          catch (Exception e) {
            throw new RuntimeException("IO Error (brutal exit)");
        }
          
          return tokens;


    	
    }
    static class ParamObject {
    	private boolean PrintAST = false;
    	private boolean PrintTokens = false;
    	private String filename = null;
    	private String library = null;
    	private boolean dumpSym = false;
    	private boolean PrintLIR = false;
    	
    	public ParamObject(String[] args) throws Exception {
    		if (args.length < 1)
    			throw new Exception("Usage: <filename> [-L<lib] [-print-ast] [-print-tokens]");
    		this.filename = args[0];
    		
    		for (int i=1; i<args.length; i++) {
    			String param = args[i];
    			if (param.equals("-print-ast")) {
    				this.PrintAST = true;
    				continue;
    			}
    			if (param.equals("-print-tokens")) {
    				this.PrintTokens = true;
    				continue;
    			}
    			if (param.startsWith("-L")) {
    				this.library = param.substring(2);
    				continue;
    			}
    			if (param.equals("-dump-symtab")) {
    				this.dumpSym = true;
    				continue;
    			}
    			if (param.equals("-print-lir")) {
    				this.PrintLIR = true;
    				continue;
    			}
    			throw new Exception("ICCompiler: Bad parameters passed to Compiler '"+param+"'");
    		}
    	}
    	public String getFilenameToCompile() {
    		return this.filename;
    	}
    	public String getLibraryToLink() {
    		return this.library;
    	}
    	public boolean shouldPrintTokens() {
    		return this.PrintTokens;
    	}
    	public boolean shouldPrintAST() {
    		return this.PrintAST;
    	}
    	public boolean shouldPrintSymTable() {
    		return this.dumpSym;
    	}
    	public boolean shouldPrintLIR() {
    		return this.PrintLIR;
    	}
    	public String getLIRname() {
    		return "c:\\1\\1.lir";
    	}
    	
    }
}